Skip to content

feat(mcp): bring-your-own MCP servers for agent sessions - #1892

Merged
simple-agent-manager[bot] merged 12 commits into
mainfrom
sam/use-sam-mcp-tools-1mbm5x
Aug 23, 2026
Merged

feat(mcp): bring-your-own MCP servers for agent sessions#1892
simple-agent-manager[bot] merged 12 commits into
mainfrom
sam/use-sam-mcp-tools-1mbm5x

Conversation

@simple-agent-manager

Copy link
Copy Markdown
Contributor

Summary

Ships MCP Servers — users connect any third-party MCP endpoint (Zapier, executor.sh, Composio, Klavis, or an official GitHub/Notion/Linear/Stripe endpoint) and SAM injects it into every agent session alongside its own sam-mcp. SAM builds no per-service connectors: the user does the OAuth in their provider's dashboard, and SAM stores only url + optional bearer token.

Phase 1 of idea 01M0QDASJCK3YWVX1GETZTSFWZ.

Named "MCP Servers", not "Connections" — the idea proposed "Connections", but that word already means composable-credential connections in SAM (SettingsConnections.tsx manages LLM + cloud provider credentials). "MCP server" is also the vocabulary users know from Claude Code, Codex and Cursor.

Critical implementation notes

The URL is a secret, not just the token. Composio and others issue pre-signed MCP URLs with the credential in the path/query, so encrypted_url is AES-256-GCM encrypted exactly like the token and is never returned by any read path. url_host (scheme + host only) is the display value.

Rule 61 — every producer of mcpServers enumerated:

# Producer Handled
1 agent-session-bootstrap.ts:271 (create) buildSessionMcpServers
2 agent-session-bootstrap.ts:329 (start) ✅ same resolved list
3 routes/workspaces/agent-sessions.ts (manual workspace session) buildSessionMcpServers
4 trial-orchestrator/steps.ts (anonymous trial) deliberately excluded — runs as the anonymous sentinel user, which owns no connections; resolving by that identity could only surface rows that do not belong to the visitor. Asserted by test.

Producers 1–2 are the shared bootstrap, which serves both the VM (TaskRunner) and cf-container (Instant) runtimes — so one implementation covers both.

Rule 54 — rollout is additive. McpServerEntry.name is optional; a vm-agent built before this field ignores it and falls back to its legacy positional naming, producing byte-identical config. VM_AGENT_REQUIRED_VERSION is generated from the deploy SHA, so new sessions only land on new agents anyway.

Pre-existing bugs fixed along the way

  1. Codex startup aborted if any injected MCP server lacked a bearer token (session_host_startup.go). One no-auth connection (Composio pre-signed URL) would have broken every Codex session for that user. Now scoped to the reserved sam-mcp entry; URL and CR/LF checks still apply to all servers.
  2. Three copies of the server-naming rule, already drifted. buildAcpMcpServers, codexMcpServerName, and an inline literal in generateVibeConfig — the last named a lone server sam-mcp-0 where the other two said sam-mcp. Consolidated into ResolveMcpServerNames.

Validation

  • pnpm lint
  • pnpm typecheck
  • pnpm test — api 8093/8093, web 3450/3450, shared 594/594, 0 collection errors (reconciled per rule 02: totals rose from the 8070/3449 baseline by exactly the tests added)
  • Additional validation: go build ./... && go vet ./... && go test ./... (25 packages), pnpm build, pnpm check:fast, pnpm quality:migration-safety, pnpm quality:do-migration-safety, pnpm quality:wrangler-bindings
  • N/A — this PR does not change candidate selection for any sweep/cron/alarm loop.

Guards proven discriminating (mutation-tested, then restored):

Guard removed Expected red Result
Scope predicate in requireScopedConnection 3 cross-scope attack tests ✅ exactly those, owner controls stayed green
Per-row try/catch in resolution 4 fault-isolation tests ✅ exactly those
secret:writeproject:read on routes 4 write-capability cases ✅ exactly those, read cases green
BYO entries dropped in the bootstrap bootstrap wiring tests ✅ (this previously left 124 tests green — see below)
Name dropped in any of 3 Go conversions round-trip test ✅ each of the three independently
Codex token precondition (both directions) relaxation / fail-closed tests ✅ both
Go name rule allowed _ cross-language contract

Staging Verification (REQUIRED)

  • Staging deployment green — run 32651936308, conclusion success. Migration 0120_mcp_connections.sql confirmed applied via D1; mcp_connections table present with all 13 columns.
  • Live app verified via Playwrightapp.sammy.party, authenticated via POST api.sammy.party/api/auth/token-login
  • Existing workflows confirmed working — 10/10 regression checks
  • New feature verified on staging — see evidence below
  • Infrastructure verification completed — VM provisioned, heartbeat confirmed, deleted
  • Mobile and desktop verification notes added

Staging Verification Evidence

CRUD + security invariants — 16/16 against the live API and UI:

PASS  token-login / session established
PASS  POST creates a connection (201)
PASS  response omits the secret URL path     ← the load-bearing assertion
PASS  response omits the bearer token        ← the load-bearing assertion
PASS  urlHost is host-only — https://mcp.example.com
PASS  reserved name sam-mcp rejected (400)
PASS  loopback without a port rejected (400) ← the TS/Go parity fix
PASS  plain http rejected (400)
PASS  list omits secrets too
PASS  UI lists the connection / UI never renders the secrets
PASS  no horizontal overflow (desktop + mobile)

Infrastructure verification (rule 6b). Staging had zero nodes before deploy (rule 27 precondition satisfied), so the test node downloaded the new binary. Provisioned node 01M0QRNAWVBGB0EDTAS2TQCBA7 reported agent_version: 1d0e94ce2710e76b4e6091f72e9474ecebfd0713this branch's HEAD — confirming both heartbeat and that the new agent build was actually running.

Injection proof — discriminating, from the real vm-agent logs:

Time Session Path Connection MCP servers registered
17:00:38 01M0QS2A28... bootstrap (VM runtime, producer 1–2) enabled 2
17:02:04 01M0QS4Y4J... manual workspace route (producer 3) enabled 2
17:02:13 01M0QS575G... manual workspace route (producer 3) disabled 1

count: 2 is sam-mcp + the user's connection. The 2→1 drop when the connection is disabled is what makes this discriminating rather than a coincidence — and it independently exercises the second producer. Grepped the full log payload: neither the secret URL path nor the bearer token appears anywhere.

Cleanup: node deleted, test connection deleted, verified 0 active nodes and 0 rows remaining. (Hetzner capacity is shared with production — 10 servers.)

Screenshots: staging-mcp-settings-{desktop,mobile}.png, staging-mcp-project-runtime-{desktop,mobile}.png.

What was NOT verified on staging, and why

An agent successfully calling a tool on a live third-party endpoint was not verified — that needs a real provider credential, which is not available. It is covered locally: mcp-connection-injection.test.ts runs a real HTTP JSON-RPC MCP server (initialize / tools/list / tools/call, bearer auth) and drives the full resolve → decrypt → compose path against it, proving the credential SAM injects actually authorizes. Combined with the staging injection evidence above, the only unproven link is the agent's own MCP client, which is not SAM code.

UI Compliance Checklist

  • Mobile-first layout verified (375×667 and 1280×800)
  • Accessibility checks completed — labelled inputs, per-row aria-label on delete, role="alert" error state, focus-trapping ConfirmDialog instead of window.confirm
  • Shared UI components used — Input, Select, Button, Alert, Spinner, StatusBadge, ConfirmDialog
  • Playwright visual audit run locally — 28/28 across both viewports, all six scenarios; screenshots in .codex/tmp/playwright-screenshots/

Review caught that the first cut used text-fg, border-border, bg-bg, bg-bg-subtlenone of which exist in the theme. They compiled to zero rules; the screenshots only looked correct through inherited colors. Fixed to real tokens.

Note for future audits: Playwright's outputDir is the screenshot directory, so running one project wipes the other's captures — run both in a single invocation.

End-to-End Verification

  • Data flow traced with code path citations
  • Capability test exercises the complete happy path across system boundaries
  • All assumptions verified against code
  • Gaps documented

Data Flow Trace

1. User submits the form
   → apps/web/src/components/mcp-servers/McpServersManager.tsx:handleCreate()
   → apps/web/src/lib/api/mcp-connections.ts:createMcpConnection()
   → POST /api/mcp-connections | POST /api/projects/:projectId/mcp-connections

2. Route authorizes and validates
   → apps/api/src/routes/mcp-connections.ts:requireScope()  (secret:write for project writes)
   → apps/api/src/services/mcp-connections.ts:createMcpConnection()
   → validateMcpConnectionName / validateMcpConnectionUrl / validateToken
   → services/encryption.ts:encrypt()  ×2 (url + token)
   → D1 mcp_connections

3. Agent session starts
   → services/agent-session-bootstrap.ts:startSamAwareAgentSession()
   → services/mcp-connection-resolution.ts:buildSessionMcpServers()
   → resolveMcpServersForSession() → decryptAndMerge() → toEntry() (per-row isolated)

4. Sent to the VM
   → services/node-agent.ts:serializeMcpServers()
   → POST {vm-agent}/workspaces/:id/agent-sessions  and  .../start

5. VM agent receives and persists
   → internal/server/workspaces.go:normalizeMcpServers() → registerSessionMcpServers()
   → internal/persistence/store.go:UpsertSessionMcpServers()  (migrateV12 adds `name`)

6. Injected into the harness
   → internal/acp/mcp_server_names.go:ResolveMcpServerNames()   ← single naming source of truth
   → Claude Code: internal/acp/session_host.go:buildAcpMcpServers() (ACP handshake)
   → Codex:       internal/acp/gateway.go:generateCodexMcpConfig() (config.toml + bearer env var)
   → Vibe:        internal/acp/gateway.go:generateVibeConfig()
   → Amp:         internal/acp/session_host.go:buildAmpMcpServer() (mcp-remote bridge)

Untested Gaps

  • Agent tool-calling against a live third-party provider — see "What was NOT verified on staging" above.
  • OpenCode and Gemini CLI reach MCP only through the generic ACP handshake with no harness-specific config; not asserted per-agent. The docs were corrected to stop claiming coverage I had not verified.

Post-Mortem

N/A: not a bug fix. This is a feature PR. It does fix two pre-existing latent bugs (Codex tokenless hard-fail; three drifted naming implementations) which are described under "Pre-existing bugs fixed along the way" — neither had shipped a user-visible failure, because both were unreachable until N>1 MCP servers existed.

Specialist Review Evidence

  • All local reviewers completed and findings addressed before merge
  • N/A — no reviewer failed to complete
Reviewer Status Outcome
security-auditor ADDRESSED 2 HIGH fixed in e3224c631: (a) the vm-agent's URL validation error embedded the plaintext URL via %q, and that error lands in tasks.error_message — a plaintext column any member with task:read, including viewers, can read; (b) TS/Go validator divergence (case-sensitivity + loopback port) made a saveable URL break every session start. 1 HIGH deferred with justification (see Exceptions). MEDIUM Vibe-cleartext and SSRF documented in the guide.
test-engineer ADDRESSED Proved by mutation that dropping every BYO entry in the bootstrap left all 124 MCP tests green — the slice called the composition function directly and every bootstrap-driving test mocked the DB. Added mcp-connection-bootstrap-wiring.test.ts, verified to fail on that exact mutation.
task-completion-validator ADDRESSED Verdict was FAIL on T7 (route authz tests absent) and T5 (slice one hop short). Both written and verified discriminating.
cloudflare-specialist ADDRESSED Drizzle declared FULL unique indexes where the migration creates PARTIAL ones — would have wrongly forbidden a personal and a project connection sharing a name. Route tests added; decrypts parallelised.
go-specialist ADDRESSED ResolveMcpServerNames honoured the reserved sam-mcp name from any index, letting a third party occupy SAM's trusted namespace. Now enforced at index 0 by the vm-agent itself (rule 51).
ui-ux-specialist ADDRESSED System consistency scored 3/5 (needs ≥4): non-existent token classes, raw inputs, window.confirm, hand-rolled chip. All four fixed.
architecture-reviewer ADDRESSED buildSamMcpEntry extracted (last duplicated https://api.${BASE_DOMAIN}/mcp literal); inaccurate docstring corrected; cross-reference to the sibling runtime-assets system added.
constitution-validator PASS No Principle XI violations. Both advisories addressed: name-length constant extracted, cross-language pinning test added.
doc-sync-validator ADDRESSED Corrected an agent-support claim I could not verify; documented the loopback port + hyphen rules; added the three env vars to .env.example and configuration.md; annotated the superseded backlog task.
performance-reviewer ADDRESSED +1 D1 round-trip (within the ≤12 mutation budget). Decrypts now run concurrently and the read is bounded — up to ~100 sequential AES-GCM ops sat on the Instant start path, which has documented timeout history (rule 43).

Exceptions

  • Scope: The Amp harness passes the MCP endpoint URL as a positional CLI argument (buildAmpMcpServer), so it is readable via /proc inside the workspace. The bearer token is already kept out of argv for exactly this reason.

  • Rationale: Fixing it requires knowing how mcp-remote accepts a URL from the environment. I could not verify that without running it, and rule 30 forbids shipping an unverified config mechanism. Marginal exposure: that agent already holds the URL and the user has shell access in their own workspace. Tracked as idea 01M0QQ7PTBDPG0DVR10XMKB679, with a comment at the call site and a caveat in the public docs (rule 42 — not a silent deferral).

  • Expiration: Before Amp is recommended for pre-signed-URL providers.

  • Scope: apps/api/src/services/node-agent.ts is 908 lines, over rule 18's 800-line hard limit.

  • Rationale: It was already 888 lines before this PR; this change adds ~20. Splitting it touches many importers and would make this diff materially harder to review. Tracked in the same idea.

  • Expiration: Next change to that file.

Agent Preflight (Required)

  • Preflight completed before code changes

Classification

  • external-api-change
  • cross-component-change
  • business-logic-change
  • public-surface-change
  • docs-sync-change
  • security-sensitive-change
  • ui-change
  • infra-change

External References

Official documentation consulted for the provider landscape and the MCP endpoint shape: Model Context Protocol, Zapier MCP, executor.sh, Composio hosted MCP platforms, Klavis, Codex remote MCP config, Claude Code MCP. Provider/ToS reality checks (LinkedIn API limits, Medium API closure) are cited in idea 01M0QDASJCK3YWVX1GETZTSFWZ.

Codebase Impact Analysis

  • packages/sharedsrc/types/mcp-connection.ts (new), src/vm-agent-contract.ts (additive name), src/constants/defaults.ts, src/fixtures/mcp-server-name-contract.json (new)
  • apps/apisrc/db/schema.ts, src/db/migrations/0120_mcp_connections.sql (new), src/services/mcp-connections.ts + mcp-connection-resolution.ts (new), src/services/node-agent.ts, src/services/agent-session-bootstrap.ts, src/routes/mcp-connections.ts (new), src/routes/workspaces/agent-sessions.ts, src/durable-objects/trial-orchestrator/steps.ts, src/services/limits.ts, src/env.ts, src/schemas/
  • packages/vm-agentinternal/acp/mcp_server_names.go (new), internal/acp/gateway.go, internal/acp/session_host.go, internal/acp/session_host_startup.go, internal/persistence/store.go, internal/server/workspaces.go, internal/server/agent_ws.go
  • apps/websrc/components/mcp-servers/, src/pages/SettingsMcpServers.tsx, src/pages/Settings.tsx, src/pages/ProjectSettings.tsx, src/App.tsx, src/lib/api/mcp-connections.ts, src/lib/query-options/mcp-connections.ts
  • apps/wwwsrc/content/docs/docs/guides/mcp-servers.md (new), src/content/docs/docs/reference/configuration.md, astro.config.ts

Documentation & Specs

  • apps/www/src/content/docs/docs/guides/mcp-servers.md (new public guide, registered in the sidebar)
  • apps/www/src/content/docs/docs/reference/configuration.md (three new env vars)
  • apps/api/.env.example
  • CLAUDE.md (Recent Changes: byo-mcp-servers)
  • tasks/archive/2026-08-23-byo-mcp-servers.md; tasks/backlog/2026-02-15-user-configurable-mcp-servers.md annotated as superseded

Constitution & Risk Check

Principle XI (No Hardcoded Values): three new limits ship as DEFAULT_* constants with env overrides, threaded to their enforcement points. All URLs derive from BASE_DOMAIN via the single buildSamMcpEntry. Name-length is a named constant, and the TS/Go copies are pinned together by a shared fixture.

Key risks and how they are handled:

  • Third-party MCP tools are a prompt-injection surface — connections are explicit opt-in, never platform-seeded; documented in the guide's Security section.
  • A bad connection row bricking session start — per-row skip-and-warn plus a shape guard; resolution failure degrades to "just sam-mcp" so an agent always starts (rules 41, 50).
  • Cross-tenant leakage — scope predicates in SQL, tested against a real SQLite engine with attack cases paired to owner controls (rule 28).
  • Rollout — additive contract field; old agents fall back to legacy naming; version-gated by deploy SHA (rule 54).

Phase 1 of idea 01M0QDASJCK3YWVX1GETZTSFWZ — bring-your-own MCP endpoints.
Task file rides on the feature branch because main is branch-protected.
Adds the mcp_connections table (migration 0120), shared types, CRUD and
resolution services, and widens the vm-agent MCP plumbing from one server to N.

- URL is encrypted alongside the token: providers such as Composio issue
  pre-signed MCP URLs with the credential in the path/query, so the URL is a
  secret. urlHost (scheme+host) is the display value the API returns instead.
- Resolution runs on the agent-session start path and degrades per row: one
  undecryptable connection must not brick session start (rules 41, 50).
- buildSessionMcpServers is the single composition point, called by the shared
  bootstrap (covers VM + cf-container, rule 61) and the manual workspace route.
- Trial path pinned to sam-mcp only and commented: it runs as the anonymous
  sentinel user and must never resolve connections by that identity.
- Contract addition is additive (optional name), so an old vm-agent ignores it
  and falls back to positional naming (rule 54).
…ions

Adds an optional Name to McpServerEntry so N injected servers are
distinguishable to the agent (zapier__create_post, not sam-mcp-1__...).

- ResolveMcpServerNames is now the single source of truth for naming. The rule
  previously existed three times (buildAcpMcpServers, codexMcpServerName, and
  inline in generateVibeConfig) and had already drifted: Vibe emitted sam-mcp-0
  for a single server where the others emitted sam-mcp. Unnamed entries keep
  the exact legacy positional behaviour, and codexMcpTokenEnvVar keeps the
  historical SAM_MCP_TOKEN / SAM_MCP_TOKEN_<n> forms.
- Codex startup required a bearer token for EVERY injected server, which would
  have made one no-auth user connection break every Codex session. The
  precondition is now scoped to the reserved sam-mcp entry; URL and CR/LF
  checks still apply to all servers.
- Name is copied through all three field-by-field conversions (normalize,
  acp->persistence, persistence->acp) plus additive migrateV12.

Tests: the round-trip test was verified to fail when any one of the three
conversions drops the field, and the Codex pair was verified discriminating in
both directions (old unscoped rule fails the relaxation test; deleting the
check fails the fail-closed controls).
Routes at /api/mcp-connections (personal) and
/api/projects/:projectId/mcp-connections (project), sharing one handler set so
the two scopes cannot drift. Project writes require secret:write, so maintainer
(which has secret:read but not secret:write) cannot store a credential every
member's agents would then use.

Tests run against a real in-memory SQLite engine rather than a .where()-ignoring
mock, since every scoping guard here IS a SQL predicate (rule 28). Verified
discriminating: deleting the scope predicate reddens exactly the three
cross-scope attack tests while the owner-path controls stay green, and removing
the per-row try/catch reddens exactly the four fault-isolation tests.

The vertical slice runs a real HTTP JSON-RPC MCP server (initialize / tools/list
/ tools/call, bearer auth) and drives the full resolve -> decrypt -> compose
path against it, proving the credential SAM injects actually authorizes. Third-
party creds are unavailable in CI; this closes the same loop without them.
One McpServersManager serves both scopes (rules 24, 59). Personal scope gets its
own Settings tab; project scope sits in the existing Runtime tab beside env vars
and files, since it is the same class of thing — configuration injected into
every agent session in that project — rather than adding another nav item.

Named 'MCP Servers', not 'Connections': that word already means
composable-credential connections in SAM (Settings -> Connections manages LLM and
cloud provider credentials), and MCP server is the vocabulary users know from
Claude Code, Codex and Cursor.

Uses TanStack Query with identity-scoped keys, and gates the spinner on 'no data
yet' rather than 'refetch in flight' (rules 48, 60).

Playwright audit fixes found by actually opening the screenshots (rule 62):
- the first-run onboarding modal covered the page, so every capture would have
  been of the modal while still reporting 'visible'; now suppressed AND asserted
  absent so the suppression cannot silently regress
- duplicate h2/h3 'MCP servers' headings (caught as a strict-mode violation)
- 'bearer token' wrapped mid-word because break-all leaked from the host onto the
  auth label; host and label are now separate spans
- the error state used a non-existent 'text-error' class and showed a bare raw
  message; now the shared Alert with context

Note for future audits: Playwright's outputDir IS the screenshot directory, so
running one project wipes the other's captures — run both in one invocation.
… shape

Adds the public docs page (provider recommendations, scopes, the security note
that third-party tools are a prompt-injection surface, and the LinkedIn/Medium
reality checks) and the CLAUDE.md changelog entry.

Updates the five existing suites that asserted the single-object mcpServer shape.
One of them surfaced a real bug rather than just a shape change: the
instant-session mocks return a non-array from the query, and decryptAndMerge was
called OUTSIDE the try/catch, so a non-array result threw straight through the
fault isolation and would have broken session start. The query, the shape check
and the merge are now all inside the guard.

Full suite: api 8070/8070, web 3449/3449, 0 collection errors in either;
lint, typecheck, build, check:fast, migration-safety, do-migration-safety,
wrangler-bindings and go vet all green.
…laim

The MCP server name rule is implemented twice — MCP_CONNECTION_NAME_PATTERN in
TypeScript (rejects on write) and sanitizeMcpServerName in Go (falls back to
positional naming). Nothing tied them together, so a drift would silently rename
a user's server to sam-mcp-<i> with no error surfaced anywhere.

Both now consume one serialized fixture (rule 23). Verified discriminating:
letting the Go side accept underscores reddens the contract test immediately.

Also corrected the docs, which claimed MCP servers reach 'every supported agent —
Claude Code, Codex, Amp, Vibe and OpenCode'. OpenCode gets no MCP config file and
only sees servers if its ACP implementation honours the handshake field, which I
have not verified. Replaced with what the code actually does, per rule 01.
Ten reviewers ran. Two HIGH and several MEDIUM findings were real bugs, not style.

SECURITY
- The vm-agent's URL validation error embedded the plaintext URL via %q. That
  error propagates into tasks.error_message / agent_sessions.error_message —
  plaintext columns any project member with task:read (including VIEWERS) can
  read. Since the URL is a secret here, this leaked a credential across a
  privilege boundary. The message now names the index only.
- The same finding exposed a second, independent validator divergence: TS
  accepted 'HTTPS://host/mcp' (WHATWG lowercases the scheme for its check) while
  Go's prefix match is case-sensitive, and TS accepted loopback without a port
  while Go required one. Either shape saved cleanly and then failed EVERY
  session start for that scope. URLs are now stored WHATWG-normalized, loopback
  requires an explicit port, and both rules are pinned by the shared contract
  fixture.
- ResolveMcpServerNames honoured the reserved 'sam-mcp' name from any index, so
  an entry claiming it would take the namespace and silently rename SAM's own
  endpoint to sam-mcp-1. The vm-agent now reserves it for index 0 itself rather
  than trusting an upstream convention (rule 51).

TESTS THAT COULD NOT SEE THE FEATURE
- Review proved by mutation that dropping every bring-your-own entry in the
  bootstrap left all 124 MCP tests green: the slice tested buildSessionMcpServers
  directly, and every test that drove the real bootstrap mocked drizzle so
  resolution always degraded to []. mcp-connection-bootstrap-wiring.test.ts now
  drives the real startSamAwareAgentSession against a real SQLite engine holding
  a real encrypted row, and was verified to fail against that exact mutation.
- The route layer had zero tests (my own T7, unmet). mcp-connections.test.ts now
  exercises the REAL project-auth against real membership rows — the sibling
  suites mock it wholesale, which cannot catch a swapped capability. Verified
  discriminating: swapping secret:write for project:read reddens exactly the four
  write cases while the read cases and owner controls stay green.

CORRECTNESS / PERF
- Drizzle declared FULL unique indexes where the migration creates PARTIAL ones;
  that difference would wrongly forbid a personal and a project connection
  sharing a name.
- Decrypts now run concurrently and the read is bounded; up to ~100 sequential
  AES-GCM ops sat on the Instant runtime's start path (rule 43 timeout history).

UI
- text-fg / border-border / bg-bg / bg-bg-subtle are not in the theme and
  compiled to nothing — the screenshots only looked right by inheritance. Now
  real tokens, plus Input/Select/StatusBadge from the design system and the
  shared ConfirmDialog instead of window.confirm.

DOCS
- Corrected the agent-support claim, documented the loopback port requirement and
  the hyphen rule, added the SSRF/DNS-rebinding residual risk, and added the three
  new env vars to .env.example and configuration.md.

api 8093/8093, web 3450/3450, shared 594/594, 0 collection errors; go test, lint,
typecheck, Playwright 28/28 all green.
…VR10XMKB679)

The one review finding not fixed in this PR gets a tracked idea, a code comment
adjacent to the code, and a user-facing caveat — rather than a silent deferral
(rule 42).

buildAmpMcpServer passes the endpoint URL as a positional CLI arg, so it is
readable via /proc by anything in the container. The token is already kept out of
argv for exactly that reason. Fixing it needs verification of how mcp-remote
accepts a URL from the environment, and shipping an unverified config mechanism
is what rule 30 forbids — so it is documented instead of guessed at.
@codspeed-hq

codspeed-hq Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing sam/use-sam-mcp-tools-1mbm5x (c94ca64) with main (69e8d12)

Open in CodSpeed

The trial-orchestrator-agent-boot test uses a mock MCP callback token
(mcp_tok_fixture_abc123) that triggers the generic-api-key rule. This is
synthetic test material, not a real secret.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@simple-agent-manager
simple-agent-manager Bot merged commit 86e94d7 into main Aug 23, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant